﻿Imports System.IO
Imports HtmlAgilityPack
Imports System.Threading.Tasks
Imports System.Net
Imports System.Globalization
Imports Microsoft.Office.Interop
Imports Microsoft.Office.Interop.Excel
Imports System.Reflection
Imports System.Runtime.InteropServices

Public Class Form1
    <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
    Public Shared Function GetWindowThreadProcessId(ByVal handle As Integer, <System.Runtime.InteropServices.Out()> ByRef processId As Integer) As Integer
    End Function
    Public Delegate Sub addListViewItemdelegate(ByVal lvitem As ListViewItem)
    Public Delegate Sub updateProgresDelegate(ByVal counters As List(Of Integer))
    Public Delegate Sub updateStatusLabelDelegate(ByVal Msg As String)
    Private ProId As Integer
    Dim stopWatch As New Stopwatch

    Dim CulturesArray() As String = {
"af", "af-ZA", "sq", "sq-AL", "ar", "ar-DZ", "ar-BH", "ar-EG", "ar-IQ", "ar-JO", "ar-KW", "ar-LB", "ar-LY", "ar-MA", "ar-OM", "ar-QA", "ar-SA", "ar-SY",
"ar-TN", "ar-AE", "ar-YE", "hy", "hy-AM", "az", "az-AZ-Cyrl", "az-AZ-Latn", "eu", "eu-ES", "be", "be-BY", "bg", "bg-BG", "ca", "ca-ES", "zh-HK", "zh-MO", "zh-CN",
"zh-CHS", "zh-SG", "zh-TW", "zh-CHT", "hr", "hr-HR", "cs", "cs-CZ", "da", "da-DK", "div", "div-MV", "nl", "nl-BE", "nl-NL", "en", "en-AU", "en-BZ", "en-CA",
"en-CB", "en-IE", "en-JM", "en-NZ", "en-PH", "en-ZA", "en-TT", "en-GB", "en-US", "en-ZW", "et", "et-EE", "fo""fo-FO", "fa", "fa-IR",
"fi", "fi-FI", "fr", "fr-BE", "fr-CA", "fr-FR", "fr-LU", "fr-MC", "fr-CH", "gl", "gl-ES", "ka", "ka-GE", "de", "de-AT", "de-DE", "de-LI", "de-LU",
"de-CH", "el", "el-GR", "gu", "gu-IN", "he", "he-IL", "hi", "hi-IN", "hu", "hu-HU", "is", "is-IS", "id", "id-ID", "it", "it-IT", "it-CH", "ja", "ja-JP",
"kn", "kn-IN", "kk", "kk-KZ", "kok", "kok-IN", "ko", "ko-KR", "ky", "ky-KZ", "lv", "lv-LV", "lt", "lt-LT", "mk", "mk-MK", "ms", "ms-BN", "ms-MY", "mr", "mr-IN",
"mn", "mn-MN", "no", "nb-NO", "nn-NO", "pl", "pl-PL", "pt", "pt-BR", "pt-PT", "pa", "pa-IN", "ro", "ro-RO", "ru", "ru-RU", "sa", "sa-IN", "sr-SP-Cyrl",
"sr-SP-Latn", "sk", "sk-SK","sl","sl-SI","es","es-AR","es-BO","es-CL","es-CO","es-CR","es-DO","es-EC","es-SV","es-GT","es-HN","es-MX",
"es-NI","es-PA","es-PY","es-PE","es-PR","es-ES","es-UY","es-VE","sw","sw-KE","sv","sv-FI","sv-SE","syr","syr-SY","ta","ta-IN","tt","tt-RU",
"te","te-IN","th","th-TH","tr","tr-TR","uk","uk-UA","ur","ur-PK","uz","uz-UZ-Cyrl","uz-UZ-Latn","vi","vi-VN"}
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim OFD As New OpenFileDialog
        With OFD
            .Title = "Facebook Messages html File Selector"
            .Filter = "htm File (*.htm)|*.htm"
            .FilterIndex = 1
            .Multiselect = False
        End With
        If OFD.ShowDialog = System.Windows.Forms.DialogResult.OK Then
            If ComboBox1.SelectedIndex >= 0 Then
                If OFD.FileName.EndsWith(".htm") Then
                    Dim SelectedCulture As String = CulturesArray(ComboBox1.SelectedIndex)
                    TextBox1.Text = OFD.FileName
                    Dim verbose As Boolean = CheckBox1.Checked
                    Dim t1 = Task.Factory.StartNew(Sub() getMessages(OFD.FileName, SelectedCulture, verbose))
                End If
            Else
                MessageBox.Show("Please Select a Localisation And Try Again", "Controle Validation", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
            End If
        End If

    End Sub
    Private Sub getMessages(ByVal filePath As String, ByVal Culture As String, ByVal verbose As Boolean)
        Dim stopWatchRef As Stopwatch
        Me.Invoke(DirectCast(Sub() stopWatchRef = stopWatch, MethodInvoker))
        Try
            stopWatchRef.Start()
            Dim HD As New HtmlDocument()
            Me.Invoke(New updateStatusLabelDelegate(AddressOf UpdateStatusLabel), New Object() {"Fetching..."})
            HD.Load(New StringReader(File.ReadAllText(filePath)))
            Dim divNodes = HD.DocumentNode.SelectNodes("//div[@class='message']")
            Dim arrayToExport(divNodes.Count - 1, 3) As String
            Dim ListviewCurentIndex As Integer
            Dim progressCounters As New List(Of Integer)
            progressCounters.Add(divNodes.Count)
            progressCounters.Add(0)
            If divNodes IsNot Nothing Then
                For Each div As HtmlAgilityPack.HtmlNode In HD.DocumentNode.SelectNodes("//div[@class='message']")
                    Dim User As String = WebUtility.HtmlDecode(div.Descendants("#text")(0).InnerText)
                    Dim meta As String = WebUtility.HtmlDecode(div.Descendants("#text")(1).InnerText)
                    Dim msgBody As String
                    Dim htmlnode As HtmlNode = div.NextSibling
                    If htmlnode.Name = "p" Then
                        msgBody = WebUtility.HtmlDecode(div.NextSibling.InnerText)
                    Else
                        msgBody = WebUtility.HtmlDecode(div.NextSibling.NextSibling.InnerText)
                    End If
                    arrayToExport(ListviewCurentIndex, 0) = User
                    arrayToExport(ListviewCurentIndex, 1) = meta
                    arrayToExport(ListviewCurentIndex, 2) = convertDateTime(meta, Culture)
                    arrayToExport(ListviewCurentIndex, 3) = msgBody
                    ListviewCurentIndex += 1
                    If verbose Then
                        Dim item(3) As String
                        item(0) = User
                        item(1) = meta
                        item(2) = msgBody
                        Dim lvitemToAdd As New ListViewItem(item)
                        Me.Invoke(New addListViewItemdelegate(AddressOf addelemntToList), New Object() {lvitemToAdd})
                    End If
                    progressCounters(1) = ListviewCurentIndex
                    Me.Invoke(New updateProgresDelegate(AddressOf updateProgress), New Object() {progressCounters})
                Next
                TwoDArrayToExcel(arrayToExport)
            End If
        Catch ex As Exception
            MessageBox.Show("Error Occured: " & ex.Message, "Application Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
    End Sub
    Private Sub addelemntToList(ByVal listitem As ListViewItem)
        LVnoFlicker1.Items.Add(listitem)
        LVnoFlicker1.Items(LVnoFlicker1.Items.Count - 1).EnsureVisible()
    End Sub
    Private Sub updateProgress(ByVal counters As List(Of Integer))
        Try
            If counters(1) = 1 Then
                ProgressStatus.Visible = True
            End If
            ProgressStatus.Maximum = counters(0)
            ProgressStatus.Value = counters(1)
            labelStatus.Text = getRemainingTime(stopWatch.ElapsedMilliseconds, counters(1), counters(0)) & " Remaining"
            If ProgressStatus.Value = ProgressStatus.Maximum Then
                stopWatch.Reset()
                stopWatch.Stop()
                ProgressStatus.Visible = False
            End If
        Catch ex As Exception
            MessageBox.Show("Error Occured: " & ex.Message, "Application Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
    End Sub
    Private Function getRemainingTime(ByVal ElapsedTimeInMillisec As Long, ByVal CurrentElement As Integer, ByVal TotalElements As Integer) As String
        Try
            Dim ETA As Long = (ElapsedTimeInMillisec / CurrentElement * (TotalElements - CurrentElement))
            Return ConvertTimeToString(ETA / 1000)
        Catch ex As Exception
            Return ""
            MessageBox.Show("Error Occured: " & ex.Message, "Application Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
    End Function
    Public Function ConvertTimeToString(ByVal seconds As Integer) As String
        Dim formattedString As String = ""
        Dim hours As Integer = seconds \ 3600
        Dim minutes As Integer = (seconds - (hours * 3600)) \ 60
        seconds = seconds - (hours * 3600 + minutes * 60)
        If hours > 0 And minutes > 0 And seconds > 0 Then
            formattedString = String.Format("{0:D2} hour(s) {1:D2} minute(s) {2:D2} Second(s)", hours, minutes, seconds)
        ElseIf hours > 0 And minutes > 0 Then
            formattedString = String.Format("{0:D2} hour(s) {1:D2} minute(s)", hours, minutes)
        ElseIf hours > 0 Then
            formattedString = String.Format("{0:D2} hour(s)", hours)
        ElseIf minutes > 0 And seconds > 0 Then
            formattedString = String.Format("{0:D2} minute(s) {1:D2} Second(s)", minutes, seconds)
        ElseIf minutes > 0 Then
            formattedString = String.Format("{0:D2} minute(s) ", minutes)
        Else
            formattedString = String.Format("{0:D2} Second(s) ", seconds)
        End If
        Return formattedString '
    End Function
    Sub TwoDArrayToExcel(ByVal DataArray(,) As String)
        Try
            Me.Invoke(New updateStatusLabelDelegate(AddressOf UpdateStatusLabel), New Object() {"Exporting To File..."})
            Dim _excel As New Excel.Application
            Dim wBook As Excel.Workbook
            Dim wSheet As Excel.Worksheet
            wBook = _excel.Workbooks.Add()
            wSheet = wBook.ActiveSheet()
            _excel.DisplayAlerts = False
            _excel.ScreenUpdating = False
            Dim ColumnsRange As Range = wSheet.Range("A1", "D1")
            Dim ColumnValues = New String(1, 4) {}
            ColumnValues(0, 0) = "Username"
            ColumnValues(0, 1) = "Formatted Datetime"
            ColumnValues(0, 2) = "DateTime Msg"
            ColumnValues(0, 3) = "Msg Body"
            ColumnsRange.Value2 = ColumnValues
            Array.Clear(ColumnValues, 0, ColumnValues.Length)
            Dim RowsRange As Range = wSheet.Range("A2", "D" & DataArray.GetUpperBound(0) + 1)
            RowsRange.Value2 = DataArray
            wBook.SaveAs(Path.Combine(Environment.CurrentDirectory, "FaceBook_Messages_log.xlsx"), Excel.XlFileFormat.xlOpenXMLWorkbook, Missing.Value,
                                        Missing.Value, False, False, Excel.XlSaveAsAccessMode.xlNoChange,
                                        Excel.XlSaveConflictResolution.xlUserResolution, True,
                                        Missing.Value, Missing.Value, Missing.Value)
            Me.Invoke(New updateStatusLabelDelegate(AddressOf UpdateStatusLabel), New Object() {"Done !"})
            QuitExcel(wBook, _excel)
        Catch ex As Exception
            MessageBox.Show("Error Occured: " & ex.Message, "Application Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
    End Sub
    Private Function convertDateTime(ByVal inputDT As String, ByVal CultureString As String) As String
        Try
            Dim provider As New CultureInfo(CultureString)
            Dim datetimeparts = inputDT.Split(" ")
            Dim trimedDate As String = datetimeparts(0) & " " & datetimeparts(1) & " " & datetimeparts(2) & " " & datetimeparts(3) & " " & datetimeparts(4)
            If trimedDate.Length > 0 Then
                Dim dayLenght As Integer = trimedDate.Split(" ")(1).Length
                Dim dateFormat As String
                If dayLenght = 1 Then
                    dateFormat = "dddd d MMMM yyyy, HH:mm"
                Else
                    dateFormat = "dddd dd MMMM yyyy, HH:mm"
                End If
                Dim dt = DateTime.ParseExact(trimedDate, dateFormat, provider, DateTimeStyles.None)
                Return dt.ToString("yyyy-MM-dd HH:mm")
            End If
        Catch ex As Exception
            MessageBox.Show("Error Occured: " & ex.Message, "Application Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
        Return inputDT
    End Function
    Private Sub UpdateStatusLabel(ByVal Msg As String)
        labelStatus.Text = Msg
    End Sub
    Private Sub QuitExcel(ByRef workbook As Workbook, ByRef excelApp As Application)
        workbook.Close()
        excelApp.Quit()
        GetWindowThreadProcessId(excelApp.Hwnd, ProId)
        Dim AllProcesses() As Process = Process.GetProcessesByName("excel")
        For Each ExcelProcess As Process In AllProcesses
            If ExcelProcess.Id = ProId Then
                ExcelProcess.Kill()
            End If
        Next ExcelProcess
        AllProcesses = Nothing
    End Sub

    Private Sub CheckBox1_CheckedChanged(sender As System.Object, e As System.EventArgs) Handles CheckBox1.CheckedChanged
        If CheckBox1.Checked Then
            Dim DResults As DialogResult = MessageBox.Show("Please Note That this Operation is resource exhaustive Would you like to Continue Anyways ?", "Verbose Mode", MessageBoxButtons.YesNo, MessageBoxIcon.Question)
            If Not DResults = System.Windows.Forms.DialogResult.Yes Then
                CheckBox1.Checked = False
            End If
        End If
    End Sub
End Class
